﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://www.codeplex.com/EmlenMud
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Reflection;
using BrainTechLLC.ThreadSafeObjects;

namespace BrainTechLLC
{
    public static class ReflectionHelpers
    {
        public static bool? BoolInvokeIfNotNull(this MethodInfo methodInfo, object thisObject, params object[] parameters)
        {
            if (methodInfo == null)
                return null;

            return Convert.ToBoolean(methodInfo.Invoke(thisObject, parameters));
        }

        public static object InvokeIfNotNull(this MethodInfo methodInfo, object thisObject, params object[] parameters)
        {
            if (methodInfo == null)
                return null;

            return methodInfo.Invoke(thisObject, parameters);
        }

        public static T1[] ReadAttribute<T1>(this MethodInfo t)
            where T1 : class, IReadableAttribute
        {
            ThreadSafeLookup<Type, object> lookup = Lookups.MethodInfoAttributeLookup.Get(t);
            Type t1Type = typeof(T1);

            object o;
            if (lookup.TryGetValue(t1Type, out o))
            {
                if (o == null)
                    return null;

                return o as T1[];
            }

            object[] objs = t.GetCustomAttributes(typeof(T1), true);
            if (objs != null && objs.Length > 0)
            {
                T1[] results = new T1[objs.Length];

                for (int n = 0; n < objs.Length; n++)
                    results[n] = objs[n] as T1;

                lookup.AddOrSet(t1Type, results);
                return results;
            }

            lookup.AddOrSet(t1Type, null);
            return null;
        }

        public static T1[] ReadAttribute<T1>(this PropertyInfo propInfo)
            where T1 : class, IReadableAttribute
        {
            ThreadSafeLookup<Type, object> lookup = Lookups.PropInfoAttributeLookup.Get(propInfo);
            Type t1Type = typeof(T1);

            object o;
            if (lookup.TryGetValue(t1Type, out o))
            {
                if (o == null)
                    return null;

                return o as T1[];
            }

            object[] objs = propInfo.GetCustomAttributes(typeof(T1), true);
            if (objs != null && objs.Length > 0)
            {
                T1[] results = new T1[objs.Length];

                for (int n = 0; n < objs.Length; n++)
                    results[n] = objs[n] as T1;

                lookup.AddOrSet(t1Type, results);
                return results;
            }

            lookup.AddOrSet(t1Type, null);
            return null;
        }

        public static T1[] ReadAttribute<T1>(this Type classType)
            where T1 : class, IReadableAttribute
        {
            ThreadSafeLookup<Type, object> lookup = Lookups.ClassInfoAttributeLookup.Get(classType);
            Type t1Type = typeof(T1);

            object o;
            if (lookup.TryGetValue(t1Type, out o))
            {
                if (o == null)
                    return null;

                return o as T1[];
            }

            object[] objs = classType.GetCustomAttributes(typeof(T1), true);
            if (objs != null && objs.Length > 0)
            {
                T1[] results = new T1[objs.Length];

                for (int n = 0; n < objs.Length; n++)
                    results[n] = objs[n] as T1;

                lookup.AddOrSet(t1Type, results);
                return results;
            }

            lookup.AddOrSet(t1Type, null);
            return null;
        }

        public static List<MethodInfo> ReadMethodInfoWithCustomAttribute<T1>(this Type t)
            where T1 : class, IReadableAttribute
        {
            // TODO: Cache this info?
            List<MethodInfo> results = new List<MethodInfo>();
            // TODO: private, etc?
            MethodInfo[] methods = t.GetMethods();
            foreach (MethodInfo method in methods)
            {
                T1[] attributes = method.ReadAttribute<T1>();

                if (attributes != null && attributes.Length > 0)
                    results.Add(method);
            }

            return results;
        }

        public static List<PropertyInfo> ReadPropertyInfoWithCustomAttribute<T1>(this Type t)
            where T1 : class, IReadableAttribute
        {
            List<PropertyInfo> results = new List<PropertyInfo>();

            PropertyInfo[] props = t.GetProperties();
            foreach (PropertyInfo prop in props)
            {
                T1[] attributes = prop.ReadAttribute<T1>();

                if (attributes != null && attributes.Length > 0)
                    results.Add(prop);
            }

            return results;
        }

        public static T2 ReadPropertyValue<T1, T2>(this Type t)
            where T1 : IReadableAttribute
        {
            object[] objs = t.GetCustomAttributes(typeof(T1), true);
            if (objs != null && objs.Length > 0)
            {
                T1 attr = (T1)objs[0];
                return (T2)attr.Value;
            }
            else { return default(T2); }
        }

#if NO_SILVERLIGHT

        public static PropertyDescriptor IDProperty(this Type type)
        {
            string propName; PropertyDescriptor prop;
            if (Lookups._typeIDPropertyMap.TryGetValue(type, out prop)) { return prop; }
            propName = type.ReadPropertyValue<IDProperty, string>();
            prop = TypeDescriptor.GetProperties(type)[propName];
            Lookups._typeIDPropertyMap.AddOrSet(type, prop);
            return prop;
        }

#endif
    }
}